Hygienic Macros for ACL2

نویسندگان

  • Carl Eastlund
  • Matthias Felleisen
چکیده

ACL2 is a theorem prover for a purely functional subset of Common Lisp. It inherits Common Lisp’s unhygienic macros, which are used pervasively to eliminate repeated syntactic patterns. The lack of hygiene means that macros do not automatically protect their producers or consumers from accidental variable capture. This paper demonstrates how this lack of hygiene interferes with theorem proving. It then explains how to design and implement a hygienic macro system for ACL2. An evaluation of the ACL2 code base shows the potential impact of this hygienic macro system on existing libraries and practices. 1 Unhygienic Macros Are Not Abstractions ACL2 [1] is a verification system that combines a first-order functional subset of Common Lisp with a first-order theorem prover over a logic of total functions. It has been used to model and verify large commercial hardware and software artifacts. ACL2 supports functions and logical statements over numbers, strings, symbols, and s-expressions. Here is a sample program: (defun double (x) (+ x x)) (defthm double⇒evenp (implies (integerp x) (evenp (double x)))) The defun form defines double, a function that adds its input to itself. The defthm form defines double⇒evenp, a conjecture stating that an integer input to double yields an even output. The conjecture is implicitly universally quantified over its free variable x. ACL2 validates double⇒evenp as a theorem, using the definition of double and axioms about implies, integerp, and evenp. From Common Lisp, ACL2 inherits macros, which provide a mechanism for extending the language via functions that operate on syntax trees. According to Kaufmann and Moore [2], “one can make specifications more succinct and easy to grasp . . . by introducing well-designed application-specific notation.” Indeed, macros are used ubiquitously in ACL2 libraries: there are macros for pattern matching; for establishing new homogenous list types and heterogenous structure types, including a comprehensive theory of each; for defining quantified claims using skolemization in an otherwise (explicit) quantifier-free logic; and so on. In the first-order language of ACL2, macros are also used to eliminate repeated syntactic patterns due to the lack of higher-order functions: R. Page, Z. Horváth, and V. Zsók (Eds.): TFP 2010, LNCS 6546, pp. 84–101, 2011. c © Springer-Verlag Berlin Heidelberg 2011 Hygienic Macros for ACL2 85 (defmacro defun-map (map-fun fun) ‘(defun ,map-fun (xs) (if (endp xs) nil (cons (,fun (car xs)) (,map-fun (cdr xs)))))) This macro definition captures the essence of defining one function that applies another pointwise to a list. It consumes two inputs,map-fun and fun, representing function names; the body constructs a suitable defun form. ACL2 expands uses of defun-map, supplying the syntax of its arguments as map-fun and fun, and continues with the resulting function definition. Consider the following term: (defun-map map-double double) Its expansion fills the names map-double and double into defun-map’s template: (defun map-double (xs) (if (endp xs) nil (cons (double (car xs)) (map-double (cdr xs))))) Unfortunately, ACL2 macros are unhygienic [3], meaning they do not preserve the meaning of variable bindings and references during code expansion. The end result is accidental capture that not only violates a programmer’s intuition of lexical scope but also interferes with logical reasoning about the program source. In short, macros do not properly abstract over syntax. To make this concrete, consider the or macro, which encodes both boolean disjunction and recovery from exceptional conditions, returning the second value if the first is nil: (defthm excluded-middle (or (not x) x)) (defun find (n xs) (or (nth n xs) 0)) The first definition states the law of the excluded middle. Since ACL2 is based on classical logic, either (not x) or x must be true for any x. The second defines selection from a list of numbers: produce the element of xs at index n, or return 0 if nth returns nil, indicating that the index is out of range. A natural definition for or duplicates its first operand: (defmacro or (a b) ‘(if ,a ,a ,b)) (1) This works well for excluded-middle, but the expanded version of find now traverses its input twice, doubling its running time: (defun find (n xs) (if (nth n xs) (nth n xs) 0)) Macro users should not have to give up reasoning about their function’s running time. Consequently, macros should avoid this kind of code duplication. The next logical step in the development of or saves the result of its first operand in a temporary variable: (defmacro or (a b) ‘(let ((x ,a)) (if x x ,b))) (2) 86 C. Eastlund and M. Felleisen This macro now produces efficient and correct code for find. Sadly though, the expanded form of excluded-middle is no longer the expected logical statement: (defthm excluded-middle (let ((x (not x))) (if x x x))) The or macro’s variable x has captured excluded-middle’s second reference to x. As a result, the conjecture is now equivalent to the statement (not x). ACL2 resolves this issue by dealing with the or macro as a special case. For symbolic verification, or expands using code duplication. For execution, it expands by introducing a fresh variable. The regular macro language of ACL2 does not come with the same expressive power, however. Allowing the creation of fresh variables would introduce uninterned symbols that violate ACL2’s axioms and thus corrupt its carefully crafted logical foundation; allowing a separation of executable behavior from the logical semantics would also invite unsoundness. The case-match macro, also provided with ACL2, does not have any such special cases. This macro is used for pattern matching and case dispatch. Its implementation is designed to work around ACL2’s lack of hygiene: the macro’s expansion never binds any temporary variables. Here is an example use of casematch to destructure a 3-element list: (let ((x (quote (1 2 3)))) (case-match x ((a b c) (list a b c)))) The macro expands into the following code: (let ((x (quote (1 2 3)))) (if (if (consp x) (if (consp (cdr x)) (if (consp (cdr (cdr x))) (eq (cdr (cdr (cdr x))) nil) nil) nil) nil) (let ((a (car x)) (b (car (cdr x))) (c (car (cdr (cdr x))))) (list a b c)) nil)) Note that the input to case-match is a variable. The macro requires that the user bind the input to a variable, because the input is duplicated many times in the macro’s output and the macro cannot safely bind a variable itself. Applications of car and cdr to walk down the input list are duplicated for the same reason; as a result, the size of the output increases quadratically. In a hygienic system, case-match would be able to safely bind temporary variables in its expanded form. Thus, the user would not need to explicitly bind the input to case-match to a variable: (case-match (quote (1 2 3)) ((a b c) (list a b c))) This also makes case-match available for use by other macros. In ACL2’s unhygienic macro system, other macros cannot safely bind a variable to store casematch’s input without risking unintended capture. Hygienic Macros for ACL2 87 Furthermore, the intermediate results of car and cdr could be bound to temporary variables, yielding fewer function calls in the expanded code. Here is the expansion of the above use of case-match produced by one possible implementation in a hygienic macro system: (let ((x0 (quote (1 2 3)))) (flet ((fail0 () nil)) (if (consp x0) (let ((x1 (car x0)) (y1 (cdr x0))) (if (consp y1) (let ((x2 (car y1)) (y2 (cdr y2))) (if (consp y2) (let ((x3 (car y2)) (y3 (cdr y2))) (if (eq y3 nil) (let ((a x1) (b x2) (c x3)) (list a b c)) (fail0)))) (fail0))) (fail0)) (fail0)))) This version of case-match uses temporary variables to perform each car and cdr only once, producing output with a linear measure. In general, macro writers tread a fine line. Many macros duplicate code to avoid introducing a variable that might capture bindings in the source code. Others introduce esoteric temporary names to avoid accidental capture. None of these solutions is universal, though. Finding itself in the same place, the Scheme community introduced the notion of hygienic macros [3,4,5]. This paper presents an adaptation of hygienic macros to ACL2. It motivates the design and the ACL2-specific challenges, sketches an implementation, and finally presents a comprehensive evaluation of the system vis-a-vis the ACL2 code base. 2 The Meaning of Hygiene for ACL2 Hygienic macro systems ensure that variables in macro-generated code respect the intended lexical scope of the program. Hence, our first step is to analyze the notion of lexical scope in ACL2 and to formulate appropriate goals and policies for the adaptation of hygienic expansion. This section presents the design goals and interprets them in the context of ACL2.

برای دانلود متن کامل این مقاله و بیش از 32 میلیون مقاله دیگر ابتدا ثبت نام کنید

ثبت نام

اگر عضو سایت هستید لطفا وارد حساب کاربری خود شوید

منابع مشابه

Polymorphic Types in ACL2

This paper describes a tool suite for the ACL2 programming language which incorporates certain ideas from the Hindley-Milner paradigm of functional programming (as exemplified in popular languages like ML and Haskell), including a “typed” style of programming with the ability to define polymorphic types. These ideas are introduced via macros into the language of ACL2, taking advantage of ACL2’s...

متن کامل

Fully-parameterized, first-class modules with hygienic macros

It is possible to define a formal semantics for configuration, elaboration, linking, and evaluation of fully-parameterized first-class modules with hygienic macros, independent compilation, and code sharing. This dissertation defines such a semantics making use of explicit substitution to formalize hygienic expansion and linking. In the module system, interfaces define the static semantics of m...

متن کامل

Formal verification of VHDL using VHDL-like ACL2 models

When a design reaches the register transfer level, essential architectural decisions have been taken; their validation required extensive simulation of the abstract behavioral specifications. The recognized need for formal verification cannot be met by current automatic equivalence and model checking tools, which mainly apply to logic synthesis inputs and outputs, or require manual abstraction....

متن کامل

A Theory of Hygienic Macros

Hygienic macro systems, such as Scheme’s, automatically rename variables to prevent unintentional variable capture—in short, they “just work.” Yet hygiene has never been formally presented as a specification rather than an algorithm. According to folklore, the definition of hygienic macro expansion hinges on the preservation of alphaequivalence. But the only known notion of alpha-equivalence fo...

متن کامل

A Macro for Reusing Abstract Functions and Theorems

Even though the ACL2 logic is first order, the ACL2 system offers several mechanisms providing users with some operations akin to higher order logic ones. In this paper, we propose a macro, named instance-of-defspec, to ease the reuse of abstract functions and facts proven about them. Defspec is an ACL2 book allowing users to define constrained functions and their associated properties. It cont...

متن کامل

ذخیره در منابع من


  با ذخیره ی این منبع در منابع من، دسترسی به آن را برای استفاده های بعدی آسان تر کنید

برای دانلود متن کامل این مقاله و بیش از 32 میلیون مقاله دیگر ابتدا ثبت نام کنید

ثبت نام

اگر عضو سایت هستید لطفا وارد حساب کاربری خود شوید

عنوان ژورنال:

دوره   شماره 

صفحات  -

تاریخ انتشار 2010